package com.wr.hangzhounewlight.opcua;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.*;


/**
 * @author yuanzhizhuo
 * createDate 2025/9/24
 */
@Slf4j
@Component
public class OpcUaClientManager {

    private final Set<String> opcUaUrlSet = Set.of(
            "opc.tcp://localhost:4840"
    );
    private final Set<String> errorOpcUaUrl = new ConcurrentSkipListSet<>();
    private final Map<String, OpcUaClient> opcUaClientMap = new ConcurrentHashMap<>();
    private final ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    /**
     * 初始化opc ua client
     */
//    @PostConstruct
    private void initOpcUaClient() {
        opcUaUrlSet.forEach(opcUaUrl -> executorService.submit(() -> {
            log.info("正在初始化opc ua url: {}", opcUaUrl);
            Optional<OpcUaClient> opcUaClientOptional = createThenConnectOpcUaClient(opcUaUrl);
            opcUaClientOptional.ifPresentOrElse(
                    opcUaClient -> opcUaClientMap.put(opcUaUrl, opcUaClient),
                    () -> errorOpcUaUrl.add(opcUaUrl)
            );
        }));
    }

    /**
     * 尝试重新连接错误的opc ua url
     */
//    @Scheduled(initialDelay = 5000, fixedRate = 10000)
    private void reconnect() {
        HashSet<String> errorUrlSetSnapshot = new HashSet<>(errorOpcUaUrl);
        errorUrlSetSnapshot.forEach(opcUaUrl -> executorService.submit(() -> {
            log.info("正在尝试重新连接opc ua url: {}", opcUaUrl);
            Optional<OpcUaClient> opcUaClientOptional = createThenConnectOpcUaClient(opcUaUrl);
            opcUaClientOptional.ifPresent(opcUaClient -> {
                opcUaClientMap.put(opcUaUrl, opcUaClient);
                errorOpcUaUrl.remove(opcUaUrl);
            });
        }));
    }

    /**
     * 创建并尝试连接opc ua
     *
     * @param opcUaUrl opc ua url
     * @return opc ua client
     */
    private Optional<OpcUaClient> createThenConnectOpcUaClient(String opcUaUrl) {
        OpcUaClient opcUaClient;
        try {
            opcUaClient = OpcUaClient.create(opcUaUrl);
            opcUaClient.connectAsync().get(5, TimeUnit.SECONDS);
        } catch (UaException e) {
            log.error("创建opc ua client: {}失败", opcUaUrl, e);
            return Optional.empty();
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            log.error("连接opc ua: {}失败", opcUaUrl, e);
            return Optional.empty();
        }
        return Optional.of(opcUaClient);
    }

    /**
     * 获取opc ua client
     *
     * @param opcUaUrl opc ua url
     * @return opc ua client
     */
    public Optional<OpcUaClient> getOpcUaClient(String opcUaUrl) {
        OpcUaClient opcUaClient = opcUaClientMap.get(opcUaUrl);
        if (opcUaClient == null) {
            log.error("opc ua url: {}不在缓存中，正在创建", opcUaUrl);
            Optional<OpcUaClient> opcUaClientOptional = createThenConnectOpcUaClient(opcUaUrl);
            if (opcUaClientOptional.isPresent()) {
                opcUaClientMap.put(opcUaUrl, opcUaClientOptional.get());
            } else {
                errorOpcUaUrl.add(opcUaUrl);
            }
            return opcUaClientOptional;
        } else {
            return Optional.of(opcUaClient);
        }
    }

}
